package cn.pink.gatesrv.net.websocket;

import cn.pink.core.*;
import cn.pink.core.support.Log;
import cn.pink.core.support.Utils;
import cn.pink.gatesrv.SessionService;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.*;

import java.net.InetSocketAddress;

/**
 * @Author: pink
 * @Date: 2022/6/24 12:07
 */
public class WebSocketServerHandler extends SimpleChannelInboundHandler<WebSocketFrame> {
    /** 当前sessionService*/
    private SessionService service;

    private StringBuilder frameText = null;
    private ByteBuf tempByteBuf = null;

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        try {
            Channel channel = ctx.channel();
            Port port = Port.getCurrent();

            service = new SessionService(port, new Session(), channel);

            //添加到port中管理, 使用addTask保证线程安全
            port.addTask(new PortTask() {
                @Override
                public void execute(Port port) {
                    port.addService(service);
                }
            });

            Log.gate.info("create websocket conn: address={}, gate={}, port={}, sessionId={}", channel.remoteAddress(), port.getNodeId(), port.getId(), service.getId());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) {
        try {
            if (frame instanceof TextWebSocketFrame) {
                frameText = new StringBuilder();
                frameText.append(((TextWebSocketFrame)frame).text());
            } else if (frame instanceof PingWebSocketFrame) {
                ctx.channel().write(new PongWebSocketFrame(frame.content()));
            } else if (frame instanceof BinaryWebSocketFrame) {
                //protoBuf
                ByteBuf in = frame.content();
                if (tempByteBuf == null) {
                    tempByteBuf = ctx.alloc().heapBuffer();
                }
                tempByteBuf.writeBytes(in);
            } else if (frame instanceof ContinuationWebSocketFrame) {
                if (frameText != null) {
                    frameText.append(((ContinuationWebSocketFrame)frame).text());
                }

                if(tempByteBuf != null) {
                    tempByteBuf.writeBytes(frame.content());
                }
            } else {
                Log.error.error("channelRead0 error, unSupport webSocketFrame type={}", frame.getClass().getName());
                ctx.close();
                return;
            }

            if (frame.isFinalFragment()) {
                byte[] buff = ByteBufUtil.getBytes(tempByteBuf);

                //消息长度
                int len = Utils.bytesToBigEndian32(buff, 0);

                //超度超长返回
                if (len > 64 * 1024) {
                    return;
                }

                //TODO 解密客户端消息  目前不加密  之后一定要加密  要不非常不安全


                service.onChannelRead(buff);

                if(frameText != null) {
                    frameText = null;
                }

                if(tempByteBuf != null) {
                    tempByteBuf.clear();
                }
            }
        } catch (Exception e) {
            InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress();
            Log.error.error("channelRead0 error, ip = " + address.getAddress().getHostAddress() + ", exception = " + e);
            e.printStackTrace();
            ctx.close();
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        try {
            ctx.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        try {
            Channel channel = ctx.channel();
            Port port = Port.getCurrent();

            Log.gate.info("websocket conn exception: address={}, gate={}, port={}, sessionId={}, cause={}", channel.remoteAddress(), port.getNodeId(), port.getId(), service.getId(), cause);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        try {
            Channel channel = ctx.channel();
            Port port = Port.getCurrent();

            Log.gate.info("websocket conn inactive: address={}, gate={}, port={}, sessionId={}", channel.remoteAddress(), port.getNodeId(), port.getId(), service.getId());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        try {
            Channel channel = ctx.channel();
            Port port = Port.getCurrent();

            port.addTask(new PortTask() {
                @Override
                public void execute(Port port) {
                    service.onChannelClosed();
                }
            });

            Log.gate.info("websocket conn close: address={}, gate={}, port={}, sessionId={}", channel.remoteAddress(), port.getNodeId(), port.getId(), service.getId());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
